// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/android/gin_java_bridge_value.h"

namespace content {

namespace {

    // The magic value is only used to prevent accidental attempts of reading
    // GinJavaBridgeValue from a random BinaryValue.  GinJavaBridgeValue is not
    // intended for scenarios where with BinaryValues are being used for anything
    // else than holding GinJavaBridgeValues.  If a need for such scenario ever
    // emerges, the best solution would be to extend GinJavaBridgeValue to be able
    // to wrap raw BinaryValues.
    const uint32_t kHeaderMagic = 0xBEEFCAFE;

#pragma pack(push, 4)
    struct Header : public base::Pickle::Header {
        uint32_t magic;
        int32_t type;
    };
#pragma pack(pop)

}

// static
std::unique_ptr<base::BinaryValue> GinJavaBridgeValue::CreateUndefinedValue()
{
    GinJavaBridgeValue gin_value(TYPE_UNDEFINED);
    return gin_value.SerializeToBinaryValue();
}

// static
std::unique_ptr<base::BinaryValue> GinJavaBridgeValue::CreateNonFiniteValue(
    float in_value)
{
    GinJavaBridgeValue gin_value(TYPE_NONFINITE);
    gin_value.pickle_.WriteFloat(in_value);
    return gin_value.SerializeToBinaryValue();
}

// static
std::unique_ptr<base::BinaryValue> GinJavaBridgeValue::CreateNonFiniteValue(
    double in_value)
{
    return CreateNonFiniteValue(static_cast<float>(in_value));
}

// static
std::unique_ptr<base::BinaryValue> GinJavaBridgeValue::CreateObjectIDValue(
    int32_t in_value)
{
    GinJavaBridgeValue gin_value(TYPE_OBJECT_ID);
    gin_value.pickle_.WriteInt(in_value);
    return gin_value.SerializeToBinaryValue();
}

// static
bool GinJavaBridgeValue::ContainsGinJavaBridgeValue(const base::Value* value)
{
    if (!value->IsType(base::Value::Type::BINARY))
        return false;
    const base::BinaryValue* binary_value = reinterpret_cast<const base::BinaryValue*>(value);
    if (binary_value->GetSize() < sizeof(Header))
        return false;
    base::Pickle pickle(binary_value->GetBuffer(), binary_value->GetSize());
    // Broken binary value: payload or header size is wrong
    if (!pickle.data() || pickle.size() - pickle.payload_size() != sizeof(Header))
        return false;
    Header* header = pickle.headerT<Header>();
    return (header->magic == kHeaderMagic && header->type >= TYPE_FIRST_VALUE && header->type < TYPE_LAST_VALUE);
}

// static
std::unique_ptr<const GinJavaBridgeValue> GinJavaBridgeValue::FromValue(
    const base::Value* value)
{
    return std::unique_ptr<const GinJavaBridgeValue>(
        value->IsType(base::Value::Type::BINARY)
            ? new GinJavaBridgeValue(
                reinterpret_cast<const base::BinaryValue*>(value))
            : NULL);
}

GinJavaBridgeValue::Type GinJavaBridgeValue::GetType() const
{
    const Header* header = pickle_.headerT<Header>();
    DCHECK(header->type >= TYPE_FIRST_VALUE && header->type < TYPE_LAST_VALUE);
    return static_cast<Type>(header->type);
}

bool GinJavaBridgeValue::IsType(Type type) const
{
    return GetType() == type;
}

bool GinJavaBridgeValue::GetAsNonFinite(float* out_value) const
{
    if (GetType() == TYPE_NONFINITE) {
        base::PickleIterator iter(pickle_);
        return iter.ReadFloat(out_value);
    } else {
        return false;
    }
}

bool GinJavaBridgeValue::GetAsObjectID(int32_t* out_object_id) const
{
    if (GetType() == TYPE_OBJECT_ID) {
        base::PickleIterator iter(pickle_);
        return iter.ReadInt(out_object_id);
    } else {
        return false;
    }
}

GinJavaBridgeValue::GinJavaBridgeValue(Type type)
    : pickle_(sizeof(Header))
{
    Header* header = pickle_.headerT<Header>();
    header->magic = kHeaderMagic;
    header->type = type;
}

GinJavaBridgeValue::GinJavaBridgeValue(const base::BinaryValue* value)
    : pickle_(value->GetBuffer(), value->GetSize())
{
    DCHECK(ContainsGinJavaBridgeValue(value));
}

std::unique_ptr<base::BinaryValue>
GinJavaBridgeValue::SerializeToBinaryValue()
{
    return base::BinaryValue::CreateWithCopiedBuffer(
        reinterpret_cast<const char*>(pickle_.data()), pickle_.size());
}

} // namespace content
